Appearance
2803. 生成器函数
js
function* factorial(n) {
if (n === 0) return 1;
let result = 1;
for (let i = 1; i <= n; i++) {
result *= i;
yield result; // 每次产出 i! 的值
}
}yield 的暂停语义
Generator 函数最核心的能力是:在执行过程中暂停,保留所有局部状态,之后再从暂停点继续。
调用 factorial(5) 不会执行函数体,只是创建一个 Generator 对象。.next() 才开始执行,执行到第一个 yield 时暂停并向外产出值。再次 .next() 从 yield 之后继续,直到下一个 yield 或函数结束。
这使得 Generator 更接近一个可控的状态机,推进时机由调用方决定,而不是函数自行一次性执行到底。
yield 双向通信
js
function* dialogue() {
const answer1 = yield "你是谁?";
const answer2 = yield "你从哪里来?";
return `你好,${answer1},来自${answer2}`;
}
const gen = dialogue();
console.log(gen.next().value); // "你是谁?"
console.log(gen.next("小明").value); // "你从哪里来?"
console.log(gen.next("北京").value); // "你好,小明,来自北京"第一个 .next() 产出了 yield 右侧的值。第二个 .next("小明") 将 "小明" 作为上一个 yield 表达式的结果赋给 answer1,同时执行到下一个 yield 产出新值。
注意:第一个 .next() 如果传参会被忽略——因为此时还没有 yield 在等待接收值。Generator 总是需要一个 .next() 来启动,然后才进入「产出/接收」的循环。
yield* 委托
js
function* inner() {
yield "A";
yield "B";
}
function* outer() {
yield "Start";
yield* inner(); // 委托 inner 的产出
yield "End";
}yield* 将生成控制权委托给另一个 Generator。outer 的迭代器在 yield* 处暂停,inner 的迭代器接管。inner 结束后,outer 继续执行剩余代码。
这个机制的价值在于组合:可以把一个大的 Generator 拆成多个小段,再通过 yield* 串起来。它和普通函数调用类似,但每一步都保留了可暂停性。
实际场景
Generator 在 JavaScript 中并不是日常使用的特性,但在几个场景下非常自然:
- 惰性序列:无限序列、大数据集的按需生成
- 异步流程控制:
redux-saga用它实现复杂的异步编排(每个 yield 等待一个 effect 完成) - 自定义迭代器:通过
Symbol.iterator+ Generator 实现对象的可迭代协议
